// 发布订阅模式

/**
 * 发布订阅与观察者的区别
 * 发布/订阅模式中，发布者并不维护订阅者，也不知道订阅者的存在，所以也不会直接通知订阅者，而是通知调度中心，由调度中心通知订阅者
 * 观察者模式把观察者对象维护在目标对象中的，需要发布消息时直接发消息给观察者。在观察者模式中，目标对象本身是知道观察者存在的
 * https://zhuanlan.zhihu.com/p/357911263
 */
class EventEmitter {
  constructor() {
    this.events = {};
  }
  on(name, fn) {
    if (!this.events[name]) {
      this.events[name] = [];
    }
    this.events[name].push(fn);
  }
  once(name, fn) {
    let onceFn = (...args) => {
      fn.call(this, ...args); // 执行
      this.off(name); // 取消订阅
    };
    this.on(name, onceFn);
  }
  off(name, fn) {
    if (typeof name === "undefined") {
      delete this.events;
    } else if (typeof name === "string") {
      if (typeof fn === "function") {
        this.events[name] = this.events[name].filter((event) => event !== fn);
      } else {
        delete this.events[name];
      }
    }
  }
  emit(name, ...args) {
    if (!this.events[name]) return;
    const events = this.events[name].slice();
    events.forEach((event) => {
      event.call(this, ...args);
    });
  }
}

// 测试
const test1 = (payload) => console.log("test1------" + payload);
const test2 = (payload) => console.log("test2---------" + payload);

const eventEmitter = new EventEmitter();
eventEmitter.on("test1", test1);
eventEmitter.once("test2", test2);

eventEmitter.emit("test1", "这是test1的参数");
eventEmitter.emit("test2", "你好啊");

eventEmitter.emit("test1", "这是test1的参数, 接受到了吗");
eventEmitter.emit("test2", "你好啊，test2");

eventEmitter.off("test1", test1);
eventEmitter.emit("test1", "这是test1的参数, test1移除了吗");
